/*
 * Purity.h
 *
 *  Created on: Jan 15, 2011
 *      Author: LyonsDesktop
 */

#ifndef PURITY_H_
#define PURITY_H_

#include "Interestingness.h"
#include "../../../Datasets/Dataset.h"
//#include "../../../Extern.h"
#include <math.h>
#include <vector>
//#include <hash_map>
#include <ext/hash_map>
namespace std { using namespace __gnu_cxx; }

using namespace CLEVER::Datasets;

namespace CLEVER {
namespace RegionDiscovery {
namespace Fitness {
namespace Additive {
//! Computes the purity from sampled probabilities.
template<typename T>
class Purity : public Interestingness<T>
{

public:
	int class_att;
	double threshold;
	double eta;
	bool initialized;
	Purity() : threshold(0), eta(0), initialized(false)
	{ }

	//! Compute the probabilities that any random element will be of a given class type. The sum of the returned probabilities will equal 1.
	std::vector<double> ComputeProbability(CLEVER::Datasets::Dataset<T> * rData, int rClassAtt, int UseIndex = -1, int IndexVal = -1)
	{                   //PROFILE_FUNC
		std::vector<double> classprob;
		int nSize = rData->Size();
		if (UseIndex > -1) {
			nSize = 0;
			for (int i = 0; i < rData->MetaData.data.size(); i++) {
				if ((int)rData->GetMeta(i, UseIndex) == IndexVal) {
					++nSize;
				}
			}
		}
		int iClassAtt = rClassAtt;                 //rData->MetaIndexOf(class_att);
		int nClass = rData->MetaData.attributes[class_att].Keys.size();


		std::hash_map<int, int> classmap;
		for (int i = 0; i < nClass; i++) {
			classmap[i] = 0;
		}

		for (int i = 0; i < rData->MetaData.data.size(); i++) {
			if (UseIndex > -1 && (int)rData->GetMeta(i, UseIndex) != IndexVal)
			{ continue; }

			int fClass = (int)rData->GetMeta(i, iClassAtt);
			int tempcount = classmap[fClass];
			tempcount++;
			classmap[fClass] = tempcount;
		}

		for (int i = 0; i < nClass; i++) {
			if (nSize == 0) { classprob.push_back(0); } else { classprob.push_back(((double)classmap[i]) / (double)nSize); }
		}

		return classprob;
	}


	//ComputeProbability(rData, class_att, att, class);
	std::vector<double> ComputeProbability(std::vector<double> & rData, NumericDataset * gData, NumericDataset * repSet, int rClassAtt, int UseIndex = -1, int IndexVal = -1)
	{                   //PROFILE_FUNC
		std::vector<double> classprob;
		int nSize = rData.size();
		if (UseIndex > -1) {
			nSize = 0;
			for (int i = 0; i < rData.size(); i++) {
				if ((int)rData[i] == IndexVal) {
					++nSize;
				}
			}
		}
		int iClassAtt = rClassAtt;                 //rData->MetaIndexOf(class_att);
		int nClass = gData->MetaData.attributes[class_att].Keys.size();


		std::hash_map<int, int> classmap;
		for (int i = 0; i < nClass; i++) {
			classmap[i] = 0;
		}

		for (int i = 0; i < rData.size(); i++) {
			if (UseIndex > -1 && (int)rData[i] != IndexVal)
			{ continue; }

			int fClass = (int)gData->GetMeta(i, iClassAtt);
			int tempcount = classmap[fClass];
			tempcount++;
			classmap[fClass] = tempcount;
		}

		for (int i = 0; i < nClass; i++) {
			if (nSize == 0) { classprob.push_back(0); } else { classprob.push_back(((double)classmap[i]) / (double)nSize); }
		}

		return classprob;
	}


	double Fitness(CLEVER::Datasets::DoubleArrayDataset * rData,  NumericDataset * repSet, int UseIndex = -1, int IndexVal = -1)
	{
		return 1 / 0;
	}


	//! See: Interestingness::Fitness
	double Fitness(CLEVER::Datasets::Dataset<T> * rData,  NumericDataset * repSet, int UseIndex = -1, int IndexVal = -1)
	{                   //PROFILE_FUNC
		if (!initialized) { return -1; }
		if (rData->RealData.data.empty()) { return 0; }
		double fitness = 0;
		std::vector<double> prob = ComputeProbability(rData, class_att, UseIndex, IndexVal);
		double purity = FindPurity(prob);
		if (purity > threshold) {
			fitness = purity - threshold;

			if (eta != 1.0) {
				fitness = pow(fitness, eta);
			}
		}

		return fitness;
	}


	double Fitness(std::vector<double> & rData, NumericDataset * gData,  NumericDataset * repSet, int UseIndex = -1, int IndexVal = -1)
	{                   //PROFILE_FUNC

		if (!initialized) { return -1; }
		if (gData->RealData.data.empty()) { return 0; }
		double fitness = 0;
		std::vector<double> prob = ComputeProbability(rData, gData, repSet, class_att, UseIndex, IndexVal);
		double purity = FindPurity(prob);
		if (purity > threshold) {
			fitness = purity - threshold;

			if (eta != 1.0) {
				fitness = pow(fitness, eta);
			}
		}

		return fitness;
	}


	//! Finds the purest probability.
	double FindPurity(std::vector<double> rProb)
	{                   //PROFILE_FUNC
		double purity = 0;

		for (int i = 0; i < rProb.size(); i++) {
			if (rProb[i] > purity) { purity = rProb[i]; }
		}
		return purity;
	}


	//! See: Interestingness::Precompute
	void Precompute(CLEVER::Datasets::Dataset<T> * rData)
	{                   //PROFILE_FUNC
		if (!rData || threshold < 0 || eta < 1.0) { int iCrash = 5 / 0; }

		initialized = true;
	}


	void Precompute(std::vector<double> & rData)
	{                   //PROFILE_FUNC
		if (rData.empty() || threshold < 0 || eta < 1.0) { int iCrash = 5 / 0; }

		initialized = true;
	}


};
}
}
}
}

#endif /* PURITY_H_ */
